/*
	Project:FFmpeg Media Source for Windows Media Foundation
	Part:Common
	File:ComInterfaceList.h

	Creator:ShanYe (2014-06-13)
	Last Change:ShanYe (2014-06-13)
	Rev:1.0.0
*/

#pragma once

#include "stdafx.h"
#include <list>

template<typename ComInterface>
class IComInterfaceQueue : protected std::list<ComInterface*>,public IUnknown
{
	typedef std::list<ComInterface*> supper;
private:
	ULONG RefCount = 1;
	CRITICAL_SECTION cs;
public:
	STDMETHODIMP QueryInterface(REFIID iid,void** ppvObject)
	{
		if (ppvObject == NULL)
			return E_POINTER;
		*ppvObject = NULL;
		return E_NOINTERFACE;
	}
	STDMETHODIMP_(ULONG) AddRef()
	{
		return (ULONG)InterlockedIncrement(&RefCount);
	}
	STDMETHODIMP_(ULONG) Release()
	{
		ULONG _RefCount = (ULONG)InterlockedDecrement(&RefCount);
		if (_RefCount == 0)
			delete this;
		return _RefCount;
	}
public:
	IComInterfaceQueue()
	{
		InitializeCriticalSection(&cs);
	}
	~IComInterfaceQueue()
	{
		DeleteCriticalSection(&cs);
	}
public:
	void PushIUnknown(ComInterface* p)
	{
		if (p == NULL)
			return;
		p->AddRef();
		supper::push_back(p);
	}

	void PushIUnknownNoAddRef(ComInterface* p)
	{
		if (p == NULL)
			return;
		supper::push_back(p);
	}

	ComInterface* PopIUnknown()
	{
		if (supper::empty())
			return NULL;
		ComInterface* p = supper::front();
		supper::pop_front();
		return p;
	}

	void PushIUnknownSafe(ComInterface* p)
	{
		EnterCriticalSection(&cs);
		PushIUnknown(p);
		LeaveCriticalSection(&cs);
	}

	void PushIUnknownSafeNoAddRef(ComInterface* p)
	{
		EnterCriticalSection(&cs);
		PushIUnknownNoAddRef(p);
		LeaveCriticalSection(&cs);
	}

	ComInterface* PopIUnknownSafe()
	{
		EnterCriticalSection(&cs);
		ComInterface* p = PopIUnknown();
		LeaveCriticalSection(&cs);
		return p;
	}

	SIZE_T GetSizeSafe()
	{
		EnterCriticalSection(&cs);
		SIZE_T s_t = supper::size();
		LeaveCriticalSection(&cs);
		return s_t;
	}

	void ClearSafe()
	{
		EnterCriticalSection(&cs);
		clear();
		LeaveCriticalSection(&cs);
	}

	size_t size() const
	{
		return supper::size();
	}

	void clear()
	{
		for (auto i = supper::begin();i != supper::end();++i)
		{
			auto p = *i;
			if (p)
				p->Release();
		}
		supper::clear();
	}
};